23.05.14
오늘 한 일
- 알고리즘 문제 풀이
- Nest.js 섹션 0 완료
- 쿠버네티스 스터디 종료
- 함수형 프로그래밍 스터디 6주차 공부 - 챕터 10,11 일급 함수 내용 정리
Nest.js 섹션 0 완료
Nest.js와 Express의 차이점
Nest.js는 Express 위에서 동작하는 프레임워크이다. Express의 기능을 모두 사용할 수 있고, Nest.js가 제공하는 기능을 사용할 수 있다.
Express는 라우터 위주의 설계라면, Nest.js는 모듈 위주의 설계이다.
Controller
res, req를 다루는 곳
라우터 역할을 한다.
라우터를 사용할 때는
@Get
,@Post
등의 데코레이터를 사용한다.- 데코레이터 붙이면 Nest.js가 알아서 해준다. 개발자들이 어떻게 돌아가는지 몰라도 돌아가기 때문에 거부감을 느낄 수 있다. 이를 제어의 역전(IoC)이라고 한다.
module은 직접 구성해야한다는 점에서 스프링보다 IoC가 약하다.
module 파일에 만들어둔 controller를 추가해야한다.
Provider
- Nest.js에서는 서비스를 provider라고 부른다.
- 서비스를 제공하는 역할을 한다.
@Injectable()
데코레이터를 붙여서 사용한다.
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
왜 서비스를 분리하지? 그냥 컨트롤러에 다 처리해서 리턴하면 되지 않나?
서비스의 역할은 비즈니스 로직의 분리이다. 컨트롤러는 요청을 받아서 응답을 해주는 역할만 하고, 비즈니스 로직은 서비스에서 처리한다.
이렇게 함으로써 서비스 단위에서는 req와 res에 대해서 모르고 딱 비즈니스 로직에만 집중할 수 있기 때문에 재사용하기 좋고 테스트 하기에도 좋다.
Express에서는 res.json()
형태로 리턴해주어야하는데, 이렇게 되면 테스트할 때 res를 모킹해서 해야하므로 번거로움이 있다. Nest.js에서는 서비스를 분리함으로써 이런 문제를 해결할 수 있다.
ConfigModule
dotenv 모듈 써도 되지만, 어차피 Nest의 모듈로 만들어서 사용해야하므로, Nest에서 제공하는 ConfigModule을 사용하는 것이 좋다.
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [ConfigModule.forRoot()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
loggerMiddleware
Nest에서 로그를 찍을 때 console.log
대신 Logger
객체를 쓰는 것이 좋다. 여러 곳에서 log를 찍으면 어디서 찍은건지 알기 어렵기 때문이다.
Logger 객체를 사용하면 로그를 찍는 곳을 알 수 있다.
use()
메서드를 사용해서 미들웨어를 등록할 수 있다.
// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './logger.middleware';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}
// logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { Logger } from './logger.service';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
constructor(private readonly logger: Logger) {}
use(req: Request, res: Response, next: NextFunction) {
const { ip, method, originalUrl } = req;
const userAgent = req.get('user-agent') || '';
res.on('finish', () => {
const { statusCode } = res;
const contentLength = res.get('content-length');
this.logger.log(
`${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`
);
});
next();
}
}
implements, Dependency Injection(DI)
implements
를 사용하면 무조건 해당 클래스에 있는 메서드를 구현해야한다. 강제성이 생기므로 좋다.
DI는 의존성 주입이다. 클래스에 의존성을 주입해주는 것이다. Nest에서는 @Injectable()
데코레이터를 사용해서 의존성을 주입한다.
DI를 해주면 직접 new를 사용해서 객체를 생성하지 않고 생성자에서 의존성을 주입받아서 사용할 수 있다.
- 인스턴스를 안에서 만들어버리면 결합성이 강해지기 때문에 테스트하기 어렵다. 의존성을 주입받으면 추상과 구현이 분리되기 때문에 코드가 유연해지고 테스트하기도 쉽다.
내일 할 일
- 알고리즘 문제 풀이
- Nest.js 공식 문서 읽기
- 익스텐션 개발(에러처리, UI 구현)
- 포트폴리오 및 부스트캠프 자기소개서 작성